#include "common.h"

#if (CONFIG_CORTINA_DIAG==1)

#define writel(v,a)	IO_WRITE(a,v)
#define readl(a)	IO_READ(a)

void sb_phy_program_24mhz(unsigned int base_addr)
{
	writel(0x00090006, base_addr);
	writel(0x000e0960, base_addr + 4);
	writel(0x68a00000, base_addr + 32);
	writel(0x50040000, base_addr + 44);
	writel(0x40250270, base_addr + 48);
	writel(0x00004001, base_addr + 52);
	writel(0x5e082e00, base_addr + 96);
	writel(0xf0914200, base_addr + 100);
	writel(0x4c0c9048, base_addr + 104);
	writel(0x00000373, base_addr + 108);  //REF clock selection
	writel(0x04841000, base_addr + 124);
	writel(0x000000e0, base_addr + 128);
	writel(0x04000023, base_addr + 132);
	writel(0x68001038, base_addr + 136);
	writel(0x0d181ea2, base_addr + 140);
	writel(0x0000000c, base_addr + 144);
	writel(0x0f600000, base_addr + 196);
	writel(0x400290c0, base_addr + 200);
	writel(0x0000003c, base_addr + 204);
	writel(0xc68b8300, base_addr + 208);
	writel(0x98280301, base_addr + 212);
	writel(0xe1782819, base_addr + 216);
	writel(0x00f410f0, base_addr + 220);
	writel(0xa0a0a000, base_addr + 232);
	writel(0xa0a0a0a0, base_addr + 236);
	writel(0x9fc00068, base_addr + 240);
	writel(0x00000001, base_addr + 244);
	writel(0x00000000, base_addr + 248);
	writel(0xd07e4130, base_addr + 252);
	writel(0x935285cc, base_addr + 256);
	writel(0xb0dd49e0, base_addr + 260);
	writel(0x0000020b, base_addr + 264);
	writel(0xd8000000, base_addr + 300);
	writel(0x0001ff1a, base_addr + 304);
	writel(0xf0000000, base_addr + 308);
	writel(0xffffffff, base_addr + 312);
	writel(0x3fc3c21c, base_addr + 316);
	writel(0x0000000a, base_addr + 320);
	writel(0x00f80000, base_addr + 324);

	//release AHB reset
	writel(0x00090007, base_addr);
}

static void sb_phy_program_100mhz(unsigned int base_addr)
{
	writel(0x00010006, base_addr);   	//rate prog
	writel(0x64a00000, base_addr + 32);
	writel(0x50040000, base_addr + 44);
	writel(0x40250270, base_addr + 48);
	writel(0x00004001, base_addr + 52);
	writel(0x5e002e00, base_addr + 96);
	writel(0x90914200, base_addr + 100);
	writel(0xce449048, base_addr + 104);
	writel(0x0000000b, base_addr + 108);  //REF clock selection
	writel(0x04841000, base_addr + 124);
	writel(0x000000e0, base_addr + 128);
	writel(0x04000023, base_addr + 132);
	writel(0x68000438, base_addr + 136);
	writel(0x0d181ea2, base_addr + 140);
	writel(0x0000000d, base_addr + 144);
	writel(0x0f600000, base_addr + 196);
	writel(0x400290c0, base_addr + 200);  //8b/10 enc. enable, DW-bits
	writel(0x0000003c, base_addr + 204);
	writel(0xc6496300, base_addr + 208);
	writel(0x98280301, base_addr + 212);
	writel(0xe1782819, base_addr + 216);
	writel(0x00f410f0, base_addr + 220);
	writel(0xa0a0a000, base_addr + 232);
	writel(0xa0a0a0a0, base_addr + 236);
	writel(0x9fc00064, base_addr + 240);
	writel(0x00000001, base_addr + 244);
	writel(0xd07e4130, base_addr + 252);
	writel(0x935285cc, base_addr + 256);
	writel(0xb0dd49e0, base_addr + 260);
	writel(0x0000020b, base_addr + 264);
	writel(0xd8000000, base_addr + 300);
	writel(0x0001ff1a, base_addr + 304);
	writel(0xf0000000, base_addr + 308);
	writel(0xffffffff, base_addr + 312);
	writel(0x3fc3c21c, base_addr + 316);
	writel(0x0000000a, base_addr + 320);
	writel(0x00f80000, base_addr + 324);

	//release AHB reset
	writel(0x00010007, base_addr);
}

 void sb_phy_program(int is_24mhz, int phy_number)
{
	int i,temp ;
	GLOBAL_PHY_CONTROL_t phy_control;
	PCIE_SATA_PCIE_GLBL_CMU_OK_CORE_DEBUG_13_t cmu_ok;
	PCIE_SATA_SNOW_PHY_COM_LANE_REG3_REG2_REG1_REG0_t com_lane;

/* debug_Aaron */
	serial_puts("sb_phy_program: ");
    serial_put_hex(is_24mhz);
    serial_puts("  ");
    serial_put_hex(phy_number);
    serial_puts("\n");

  /* Reset before init */
  phy_control.wrd = readl(GLOBAL_PHY_CONTROL);           
	switch (phy_number){
	case 0:
        	phy_control.bf.phy_0_por_n_i = 0;
        	phy_control.bf.phy_0_pd = 1;
        	phy_control.bf.phy_0_refclksel = 2;  /* if 24MHz */
        	phy_control.bf.phy_0_cmu_resetn_i = 0;
        	phy_control.bf.phy_0_ln0_resetn_i = 0;
		break;
	case 1:
        	phy_control.bf.phy_1_por_n_i = 0;
        	phy_control.bf.phy_1_pd = 1;
        	phy_control.bf.phy_1_refclksel = 2;  /* if 24MHz */
        	phy_control.bf.phy_1_cmu_resetn_i = 0;
        	phy_control.bf.phy_1_ln0_resetn_i = 0;
		break;
	case 2:
        	phy_control.bf.phy_2_por_n_i = 0;
        	phy_control.bf.phy_2_pd = 1;
        	phy_control.bf.phy_2_refclksel = 2;  /* if 24MHz */
        	phy_control.bf.phy_2_cmu_resetn_i = 0;
        	phy_control.bf.phy_2_ln0_resetn_i = 0;
		break;
	}
	writel(phy_control.wrd, GLOBAL_PHY_CONTROL);
  udelay(10);



	/* Release the power on reset */
	/* Register: GLOBAL_PHY_CONTROL_phy_# */
	/* Fields to write:
            por_n_i = 1'b1
            pd = 1'b0
	 If reference clock is 24MHz, then program refclksel = 2'b10
	 If reference clock is 100MHz, then program refclksel = 2'b00
	*/
	phy_control.wrd = readl(GLOBAL_PHY_CONTROL);
	switch (phy_number){
	case 0:
        	phy_control.bf.phy_0_por_n_i = 1;
        	phy_control.bf.phy_0_pd = 0;
        	phy_control.bf.phy_0_refclksel = 2;  /* if 24MHz */
		break;
	case 1:
        	phy_control.bf.phy_1_por_n_i = 1;
        	phy_control.bf.phy_1_pd = 0;
        	phy_control.bf.phy_1_refclksel = 2;  /* if 24MHz */
		break;
	case 2:
        	phy_control.bf.phy_2_por_n_i = 1;
        	phy_control.bf.phy_2_pd = 0;
        	phy_control.bf.phy_2_refclksel = 2;  /* if 24MHz */
		break;
	}
	writel(phy_control.wrd, GLOBAL_PHY_CONTROL);

	switch (phy_number){
        case 0:
		if (is_24mhz)
			sb_phy_program_24mhz(PCIE_SATA_SNOW_PHY_CMU_REG3_REG2_REG1_REG0);
		else
			sb_phy_program_100mhz(PCIE_SATA_SNOW_PHY_CMU_REG3_REG2_REG1_REG0);
		break;
	case 1:
		if (is_24mhz)
        	sb_phy_program_24mhz(PCIE_SATA_SNOW_PHY_CMU_REG3_REG2_REG1_REG0 + 0x4000);
		else
        	sb_phy_program_100mhz(PCIE_SATA_SNOW_PHY_CMU_REG3_REG2_REG1_REG0 + 0x4000);
		break;
	case 2:
		if (is_24mhz)
        	sb_phy_program_24mhz(PCIE_SATA_SNOW_PHY_CMU_REG3_REG2_REG1_REG0 + 0x8000);
		else
        	sb_phy_program_100mhz(PCIE_SATA_SNOW_PHY_CMU_REG3_REG2_REG1_REG0 + 0x8000);
		break;
    }
	/* Release the cmu reset */
	/* Register: GLOBAL_PHY_CONTROL_phy_# */
	/* Fields to write:
            cmu_resetn_i = 1'b1
	*/
	phy_control.wrd = readl(GLOBAL_PHY_CONTROL);
	switch (phy_number){
        case 0:
        	phy_control.bf.phy_0_cmu_resetn_i = 1;
		break;
	case 1:
        	phy_control.bf.phy_1_cmu_resetn_i = 1;
		break;
	case 2:
        	phy_control.bf.phy_2_cmu_resetn_i = 1;
		break;
	}
	writel(phy_control.wrd, GLOBAL_PHY_CONTROL);

	/* Wait for CMU OK */
	for (i = 0; i < 1000; i++){
        	cmu_ok.wrd = readl(PCIE_SATA_PCIE_GLBL_CMU_OK_CORE_DEBUG_13 + phy_number * 0x400);
        	if (cmu_ok.bf.phy_cmu_ok == 1)
            		break;

        	udelay(100);
	}


	/* Release Lane0 master reset */
	/* Register: Common Lane Register 0 Base address +'d200 = 32'h400290c2 */
	com_lane.wrd = 0x400290c2;
	writel(com_lane.wrd, PCIE_SATA_SNOW_PHY_COM_LANE_REG3_REG2_REG1_REG0 + phy_number * 0x4000);


	/* Release the lane reset */
	/* Register: GLOBAL_PHY_CONTROL_phy_# */
	/* Fields to write:
                ln0_resetn_i = 1'b1
	*/
	phy_control.wrd = readl(GLOBAL_PHY_CONTROL);

	switch (phy_number){
	case 0:
        	phy_control.bf.phy_0_ln0_resetn_i = 1;
		break;
	case 1:
        	phy_control.bf.phy_1_ln0_resetn_i = 1;
		break;
	case 2:
        	phy_control.bf.phy_2_ln0_resetn_i = 1;
		break;
	}
	writel(phy_control.wrd, GLOBAL_PHY_CONTROL);


        /* debug_Aaron on 2011/07/03 for ref. board IO MUX */
        temp = readl(0xf0000024);
        temp |= 0x10000000;
        writel(temp, 0xf0000024);

        temp = readl(0xf0070210);
        temp &= 0xEFFFFFFF;
        writel(temp, 0xf0070210);

        temp = readl(0xf0070214);
        temp &= ~0x10000000;
        writel(temp, 0xf0070214);
	udelay(1000);
        temp |= 0x10000000;
        writel(temp, 0xf0070214);
	udelay(1000000);

}


int g2_pcie_sbphy_init(int argc, char **argv)
{
	int port_number = 0;
	GLOBAL_PHY_CONTROL_t phy_control;

	if (argc < 3)
		return -1;

	port_number = iros_strtol(argv[1]);

	if (*argv[2] == 'i')
		sb_phy_program(1, port_number);
	else
		sb_phy_program(0, port_number);

	udelay(1000);

	 g2_pcie_read_confs(argc, argv);

	/* debug_Aaron power down SB phy, for BUG#29085 */
        phy_control.wrd = readl(GLOBAL_PHY_CONTROL);
        switch (port_number){
        case 0:
                phy_control.bf.phy_0_por_n_i = 0;
                phy_control.bf.phy_0_pd = 1;
                phy_control.bf.phy_0_refclksel = 2;  /* if 24MHz */
                break;
        case 1:
                phy_control.bf.phy_1_por_n_i = 0;
                phy_control.bf.phy_1_pd = 1;
                phy_control.bf.phy_1_refclksel = 2;  /* if 24MHz */
                break;
        case 2:
                phy_control.bf.phy_2_por_n_i = 0;
                phy_control.bf.phy_2_pd = 1;
                phy_control.bf.phy_2_refclksel = 2;  /* if 24MHz */
                break;
        }
        writel(phy_control.wrd, GLOBAL_PHY_CONTROL);


	return 0;
}

#endif /* CONFIG_CORTINA_DIAG */
